
#include "logger.h"

#include <string.h>

USE_JLIB_NS


void testCRC ();
void testVecmath ();
void testExecutor ();
void testAsm ();


ByteBuffer<>



int main (int argc, char **argv) {
  if (argc != 2)
    return -1;


//  testCRC();
//  testVecmath();
//  testExecutor();
  testCipher();
  return 0;
}



inline u64 crc (const void *data, size_t size, u64 poly, u64 init, u64 oxor, bool rev, u64 bits) noexcept {
  u64 b = 1ULL << (bits - 1);
  auto sp = (const u8*)data;
  size = size * 8 + bits;
  auto j = 7;
  u64 head = 0;
  for (auto i = 0; i < bits; ++i, --size) {
    auto byte = rev ? BitSet<u8>::swap<u8>(*sp) : *sp;
    head <<= 1;
    head = byte & (1ULL << j) ? head | 1 : head;
    if (--j < 0) {
      j = 7;
      ++sp;
    }
  }
  for (init ^= head; size > 0; --size) {
    auto byte = rev ? BitSet<u8>::swap<u8>(*sp) : *sp;
    auto div = init & b;
    init <<= 1;
    init = byte & (1ULL << j) ? init | 1 : init;
    init = div ? init ^ poly : init;
    if (--j < 0) {
      j = 7;
      ++sp;
    }
  }
  init = rev ? BitSet<u64>::swap<u64>(init) >> (64 - bits) : init;
  if (bits < 64)
    init &= (1ULL << bits) - 1;
  return init ^ oxor;
}



template<typename Type, u8 Bits, Type Poly, Type Init, Type Xor, bool Rev>
inline void crc (const char *name) {
  static const c8 *src = "123456777\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
  static const size_t len = strlen(src);
  using namespace jlib;
  Type res = CRC<Type, Poly, Init, Xor, Rev, Bits>::checksum(src, len);
  u64 check = crc(src, len, Poly, Init, Xor, Rev, Bits);
  log_i("%-16s %16u %16lX %16lX %16lX %16s %16lX %16lX", name, Bits, (u64)Poly, (u64)Init, (u64)Xor, Rev ? "Yes" : "No", (u64)res, check);
}





void testCRC () {

  log_w("%16s %16s %16s %16s %16s %16s %16s %16s", "Name", "Bit Width", "Polynomial", "Initial Value", "Final Xor", "Reverse I/O", "Result", "Check");
  crc<u8, 3, 0x3, 0x7, 0x0, true>("CRC-3/ROHC");
  crc<u8, 4, 0x3, 0x0, 0x0, true>("CRC-4/ITU");
  crc<u8, 5, 0x05, 0x1F, 0x00, true>("CRC-5/USB");
  crc<u8, 5, 0x09, 0x09, 0x00, false>("CRC-5/EPC");
  crc<u8, 5, 0x15, 0x00, 0x00, true>("CRC-5/ITU");
  crc<u8, 6, 0x03, 0x00, 0x00, true>("CRC-6/ITU");
  crc<u8, 6, 0x19, 0x00, 0x00, true>("CRC-6/DARC");
  crc<u8, 7, 0x09, 0x00, 0x00, false>("CRC-7");
  crc<u8, 7, 0x4F, 0x7F, 0x00, true>("CRC-7/ROHC");
  crc<u8, 8, 0x07, 0x00, 0x00, false>("CRC-8");
  crc<u8, 8, 0x07, 0x00, 0x00, false>("CRC-8/ITU");
  crc<u8, 8, 0x07, 0xFF, 0x00, true>("CRC-8/ROHC");
  crc<u8, 8, 0x1D, 0xFD, 0x00, false>("CRC-8/I-CODE");
  crc<u8, 8, 0x31, 0x00, 0x00, true>("CRC-8/MAXIM");
  crc<u8, 8, 0x39, 0x00, 0x00, true>("CRC-8/DARC");
  crc<u8, 8, 0x9B, 0x00, 0x00, true>("CRC-8/WCDMA");
  crc<u16, 10, 0x233, 0x000, 0x000, false>("CRC-10");
  crc<u16, 11, 0x385, 0x01A, 0x000, false>("CRC-11");
  crc<u16, 12, 0x80F, 0x000, 0x000, false>("CRC-12/3GPP");
  crc<u16, 12, 0x80F, 0x000, 0x000, false>("CRC-12/DECT");
  crc<u16, 14, 0x0805, 0x0000, 0x0000, true>("CRC-14/DARC");
  crc<u16, 15, 0x4599, 0x0000, 0x0000, false>("CRC-15");
  crc<u16, 16, 0x0589, 0x0000, 0x0000, false>("CRC-16/DECT-R");
  crc<u16, 16, 0x0589, 0x0000, 0x0000, false>("CRC-16/DECT-X");
  crc<u16, 16, 0x1021, 0x0000, 0x0000, false>("CRC-16/ACOR");
  crc<u16, 16, 0x1021, 0x1D0F, 0x0000, false>("CRC-16/ACOR");
  crc<u16, 16, 0x1021, 0xFFFF, 0x0000, false>("CRC-16/ACOR");
  crc<u16, 16, 0x1021, 0xFFFF, 0x0000, false>("CRC-16/ACOR");
  crc<u16, 16, 0x1021, 0x0000, 0x0000, true>("CRC-16/CCIT");
  crc<u16, 16, 0x1021, 0x89EC, 0x0000, true>("CRC-16/TMS37157");
  crc<u16, 16, 0x1021, 0xB2AA, 0x0000, true>("CRC-16/RIELLO");
  crc<u16, 16, 0x1021, 0xC6C6, 0x0000, true>("CRC-A");
  crc<u16, 16, 0x1021, 0xFFFF, 0x0000, true>("CRC-16/MCRF4XX");
  crc<u16, 16, 0x1021, 0xFFFF, 0x0000, true>("CRC-16/IBM-SDLC");
  crc<u16, 16, 0x3D65, 0x0000, 0x0000, false>("CRC-16/EN-13757");
  crc<u16, 16, 0x3D65, 0x0000, 0x0000, true>("CRC-16/DNP");
  crc<u16, 16, 0x8005, 0x0000, 0x0000, false>("CRC-16/BUYPASS");
  crc<u16, 16, 0x8005, 0x800D, 0x0000, false>("CRC-16/DDS-110");
  crc<u16, 16, 0x8005, 0x0000, 0x0000, true>("CRC-16/ARC");
  crc<u16, 16, 0x8005, 0x0000, 0x0000, true>("CRC-16/MAXIM");
  crc<u16, 16, 0x8005, 0xFFFF, 0x0000, true>("CRC-16/USB");
  crc<u16, 16, 0x8005, 0xFFFF, 0x0000, true>("CRC-16/MODBUS");
  crc<u16, 16, 0x8BB7, 0x0000, 0x0000, false>("CRC-16/T10-DIF");
  crc<u16, 16, 0xA097, 0x0000, 0x0000, false>("CRC-16/TELEDISK");
  crc<u32, 24, 0x5D6DCB, 0xABCDEF, 0x0000, false>("CRC-24/FLEXRAY-B");
  crc<u32, 24, 0x5D6DCB, 0xFEDCBA, 0x0000, false>("CRC-24/FLEXRAY-A");
  crc<u32, 24, 0x864CFB, 0xB704CE, 0x0000, false>("CRC-24/OPENPGP");
  crc<u32, 32, 0x000000AF, 0x00000000, 0x00000000, false>("CRC-32/XFER");
  crc<u32, 32, 0x04C11DB7, 0x00000000, 0x00000000, false>("CRC-32/POSIX");
  crc<u32, 32, 0x04C11DB7, 0xFFFFFFFF, 0x00000000, false>("CRC-32/BZIP2");
  crc<u32, 32, 0x04C11DB7, 0xFFFFFFFF, 0x00000000, false>("CRC-32/MPEG-2");
  crc<u32, 32, 0x04C11DB7, 0xFFFFFFFF, 0x00000000, true>("CRC-32/PKZIP");
  crc<u32, 32, 0x04C11DB7, 0xFFFFFFFF, 0x00000000, true>("CRC-32/JAM");
  crc<u32, 32, 0x1EDC6F41, 0xFFFFFFFF, 0x00000000, true>("CRC-32C");
  crc<u32, 32, 0x814141AB, 0x00000000, 0x00000000, false>("CRC-32Q");
  crc<u32, 32, 0xA833982B, 0xFFFFFFFF, 0x00000000, true>("CRC-32D");
  crc<u64, 40, 0x0004820009, 0x0000000000, 0x0000000000000000, false>("CRC-40/GSM");
  crc<u64, 64, 0x42F0E1EBA9EA3693, 0x0000000000000000, 0x0000000000000000, false>("CRC-64/ECMA");
  crc<u64, 64, 0x000000000000001B, 0x0000000000000000, 0x0000000000000000, false>("CRC-64/ISO");
}


void testVecmath () {
//  f32v8 m0 = {1, 0, 0, 0, 0, 1, 0, 0};
//  f32v8 m1 = {0, 0, 1, 0, 0, 0, 0, 1};
//  f32v4 v0 = {2, 3, 4, 1};
//  f32v4 v1 = vec4s_mul_mat4s(v0, m0, m1);
//  f32v4 v2 = mat4s_mul_vec4s(m0, m1, v0);
//  log_e("%f, %f, %f, %f", v1[0], v1[1], v1[2], v1[3]);
//  log_e("%f, %f, %f, %f", v2[0], v2[1], v2[2], v2[3]);
}


void testExecutor () {

  std::mutex mutex;
  std::condition_variable cv;
  Scheduler scheduler;

  scheduler.scheduleWithFixedRate(1000000, 1000000, 30, [&](int64_t step){
    log_i("#1 %ld", step);
    std::unique_lock<std::mutex> lock{mutex};
    if (step < 29)
      return;
    cv.notify_all();
  });

  scheduler.scheduleWithFixedRate(1000000, 1230000, 30, [&](int64_t step){
    log_i("#2 %ld", step);
    std::unique_lock<std::mutex> lock{mutex};
    if (step < 29)
      return;
    cv.notify_all();
  });

  std::unique_lock<std::mutex> lock{mutex};
  cv.wait(lock);
}


void testCipher () {

}